﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Text;
using System.Threading;
using Ganosoft.Aspect;
using Ganosoft.Container;
using GanoESB.Component;

namespace GanoESB.Business
{
    public class ComponentRuntime
    {
        private AppDomain domain;
        private IComponent component;
        private ComponentDescriptor descr;
        private ServiceBus serviceBus;

        private List<string> idList;
        private Dictionary<string, Assembly> assemblyList;
        private List<string> messages;
        private Dictionary<Assembly, Dictionary<string, Type>> messageTypes;

        public void RegisterMessage(string message)
        {
            messages.Add(message);
        }

        public ComponentRuntime(ComponentDescriptor descr, ServiceBus serviceBus)
        {
            this.descr = descr;
            this.serviceBus = serviceBus;

            messages = new List<string>();
            idList = new List<string>();
            assemblyList = new Dictionary<string, Assembly>();
            messageTypes = new Dictionary<Assembly, Dictionary<string, Type>>();
        }

        public ComponentDescriptor Descriptor
        {
            get { return descr; }
        }

        public string DeploymentFile 
        {
            get { return descr.DeploymentFile; }
        }

        public Dictionary<string, Type> GetMessageTypes(Type intfType)
        {
            Assembly assembly = intfType.Assembly;
            if (messageTypes.ContainsKey(assembly))
                return messageTypes[assembly];

            return null;
        }

        public Assembly LoadIntfAssembly(ServiceDescriptor descr)
        {
            //http://tech.16c.cn/dotnet/Csharp/2010011718079.html
            string[] strs = descr.ServiceInterface.Split(',');
            string dllFullName = (descr.ComponentName + "\\" + strs[1].Trim(' ') + ".dll").ToLower();
            if (!assemblyList.ContainsKey(dllFullName))
            {
                string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dllFullName);

                Assembly assembly = Assembly.LoadFile(dllPath);
                assemblyList[dllFullName] = assembly;

                // 查找所有消息
                Dictionary<string, Type> msgMap = new Dictionary<string, Type>();
                Type msgType = typeof(IMessage);
                Type[] types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    if (Array.IndexOf<Type>(type.GetInterfaces(), msgType) >= 0)
                        msgMap[type.Name] = type;
                }
                messageTypes[assembly] = msgMap;
            }

            return assemblyList[dllFullName];
        }

        internal void Start()
        {
            //if (descr.InstanceNum < 1)
            //    descr.InstanceNum = 1;


            //for (int i = 0; i < descr.InstanceNum; i++)
            //{
            //    string componentId = descr.ComponentName + "_" + i;
            //    if (descr.RunMode == 0)
            //    {
            //        C c = new C(descr, componentId);
            //        AspectContainer.Instance.Inject(c);
            //        Thread thread = new Thread(new ThreadStart(c.Start));
            //        thread.Start();
            //    }
            //    else if (descr.RunMode == 1) // outsite process
            //    {
            //        string path = AppDomain.CurrentDomain.BaseDirectory;
            //        Process process = new Process();
            //        process.StartInfo.FileName = Path.Combine(path, "ganoesb.apartment.exe");
            //        process.StartInfo.Arguments = descr.ComponentName + " " + componentId;
            //        process.Start();
            //    }
            //    idList.Add(componentId);
            //}

            C c = new C(this);
            AspectContainer.Instance.Inject(c);
            Thread thread = new Thread(new ThreadStart(c.Start));
            thread.Start();
        }

        internal void Stop()
        {
            if (component != null)
            {
                component.Stop();
                AppDomain.Unload(domain);
            }
        }

        private class C
        {
            private ComponentRuntime runtime;
            private AppDomain domain;
            private IComponent component;

            [Bean]
            protected ILogService logService;

            public C(ComponentRuntime runtime)
            {
                this.runtime = runtime;
            }

            public void Start()
            {
                try
                {
                //    AppDomainSetup info = new AppDomainSetup();
                //    info.ShadowCopyFiles = "true";
                //    //info.ShadowCopyDirectories = "";
                //    info.CachePath = "e:\\cached\\" + descr.ComponentName;

                //    string basePath = AppDomain.CurrentDomain.BaseDirectory;
                //    info.ApplicationBase = basePath;
                //    //info.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory + descr.ComponentName;
                //    AppDomain domain = AppDomain.CreateDomain(componentId, null, info);
                //    string exeFile = Path.Combine(basePath, "ganoesb.apartment.exe");
                //    domain.ExecuteAssembly(exeFile, new string[] { descr.ComponentName, componentId });
                    ComponentDescriptor descr = runtime.Descriptor;
                    AppDomainSetup setup = new AppDomainSetup();
                    setup.ApplicationName = descr.ComponentName;
                    setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
                    setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, descr.ComponentName);
                    setup.CachePath = "e:\\cached\\" + descr.ComponentName;
                    setup.ShadowCopyFiles = "true";

                    domain = AppDomain.CreateDomain(descr.ComponentName, null, setup);

                    string serviceAssembly;
                    if (string.IsNullOrEmpty(descr.ComponentClazz))
                    {
                        Type compType = typeof(StandardComponent);
                        descr.ComponentClazz = compType.FullName;
                        serviceAssembly = typeof(StandardComponent).Assembly.FullName;
                    }
                    else
                    {
                        serviceAssembly = descr.ServiceAssembly;
                    }

                    component = (IComponent)domain.CreateInstanceAndUnwrap(serviceAssembly, descr.ComponentClazz);
                    component.Start(descr);

                    runtime.domain = domain;
                    runtime.component = component;
                }
                catch (Exception ex)
                {
                    logService.Fatal(this.GetType(), ex);
                    throw ex;
                }
            }
        }
    }
}
